모노레포 버전 도입 재도전기

2025.12.02

저번에 한 번 하려고 했다가 머리가 아퍼서 그냥 접고,
develop에 코드가 올라갈 때마다 관련 없는 프로젝트까지 배포 , 이 과정에서 준비되지 않은 코드가 (develop까지 올라갔으니 경우의 수는 무척 적지만) 배포 실패가 되는 게 말이 안된다고 생각해 다시 재도전

목표

  1. 프로젝트별 서로 다른 버전으로 관리

    1. 이거 일단 버전 관리는 이번 목표에서 스킵. 여기서 버전 관리까지 하려니까 너무 꼬임.

    2. changeset을 활용하는 목적은 "변경 사항 감지"에 그칠 것. 근데 이거 turborepo 자체로 확인하는 법 없나?

      1. 있다고 함;Í

      2. turborepo는 현재 브랜치와 비교 대상(예: main 또는 이전 커밋)을 비교하여 영향을 받는 프로젝트만 필터링할 수 있음.

          # develop 브랜치와 비교해서 변경한 것과, 그것에 의존하는 앱만 빌드
          pnpm turbo build --filter=...[origin/develop]
        • ...[origin/develop]: "origin/develop 브랜치 이후로 변경된 모든 것과 그 변경사항에 의존하는 하위 프로젝트들"을 의미합니다.
        • 이 명령어를 실행하면, 변경되지 않은 앱은 자동으로 >>> FULL TURBO (캐시 히트) 가 뜨거나 실행 대상에서 제외됩니다.
      3. 그렇담 이제 changeset을 유지해야 할 이유가 없음. changeset은 업데이트를 해야 할 패키지를 명시적으로 선택할 수 있는 부분에서 낫지만, 버전 관리를 하지 않는다면 이 기능도 필요 없음.

  2. 운영/배포 서버 배포 용이하게 만들기

    • 브랜치 전략

      # main        → project-a/b 모두 production 배포
      # develop     → project-a/b 모두 development 배포
      # feature/*   → 기능 개발
      # hotfix/*    → 긴급 수정
      
    • 단순 push 말고, 버전 업데이트가 되면 배포할 수 있도록 -> stage 브랜치가 필요 없어짐.

  3. change log가 쌓이도록 <- 변경 사항이 목록으로 보이도록

    • 이 change log가 notion에 반영되면 참 좋을텐데 ...

1. 버전 관리 changeset

changeset 을 이용할거다.

목표

monorepo/
├── apps/
│   ├── project-a/
│   │   └── package.json        # version: "2.3.1"
│   └── project-b/
│       └── package.json        # version: "1.0.5"
├── packages/
│   ├── ui/
│   │   └── package.json        # version: "2.5.3"
│   └── react-hooks/
│       └── package.json        # version: "1.2.0"

시나리오 1. Feature 개발 및 develop 배포

develop 상태를 base로 할 때 기능 개발 완료 후 개발 서버에 배포한다는 가정.

순서 요약

  1. develop 기반의 feature 브랜치 생성 + 현재 버전 확인
  2. changeset 생성

--- Q. changeset 에서 major, minor를 내가 선택할 수 없음. 알아서 선택함.

  1. 변경사항 확인 및 커밋

--- Q. 이거 changeset이 먼저 커밋이 먼저 ?
--- Q. changeset 하나 당 한 기능이어야 하는건가?

  1. push 및 PR 생성 (develop으로 향함)

  2. 워크플로우 확인

  3. Feature 브랜치 생성 및 개발

# Feature 브랜치 생성
git checkout develop
git checkout -b feature/add-use-local-storage

# 수정

# 빌드 테스트
pnpm --filter @test/react-hooks build
  1. Changeset 생성

changeset 할 때 major/minor를 선택할 수 없을 때 확인해야 할 것

  1. .chanteset/config.json 설정 중 commit: false 옵션 확인
# Changeset 생성
pnpm changeset
  1. 커밋 앤 푸시

changeset과 커밋 순서: changeset이 먼저? 커밋이 먼저?

  • changeset 생성 -> 커밋해야 한다. changeset 한 뒤 생성되는 .md 문서도 코드 변경의 일부이기 때문에 함께 커밋되어야 함.

pnpm changesest 하는 기준: "배포 가능한 변경 단위"

changeset을 만들어야 하는 경우:

  1. 새로운 기능 추가
  2. 버그 수정
  3. breaking change
  4. 여러 패키지에 영향을 주는 변경

예시: 여러 작은 커밋, 하나의 changeset

# 1. Feature 브랜치
git checkout -b feature/add-debounce

# 2. 첫 번째 커밋 (Changeset 없이)
# ... useDebounce 기본 구현 ...
git add .
git commit -m "feat: implement basic useDebounce"
git push

# 3. 두 번째 커밋 (Changeset 없이)
# ... 테스트 추가 ...
git commit -m "test: add useDebounce tests"
git push

# 4. 세 번째 커밋 (문서 추가, Changeset 없이)
# ... README 업데이트 ...
git commit -m "docs: document useDebounce"
git push

# 5. 마지막에 Changeset 생성
pnpm changeset
# @test/react-hooks: minor
# "Added useDebounce hook with full test coverage"

git add .
git commit -m "chore: add changeset"
git push

# ✅ 여러 커밋 = 하나의 PR = 하나의 Changeset
# 변경사항 확인
git status

# 커밋
git add .
git commit -m "feat: add useLocalStorage hook"

# 푸시
git push -u origin feature/add-use-local-storage
  1. PR 생성 및 머지

  2. Github Actions 확인

    1. PR 머지 결과:

      ✅ Changeset 감지
      ✅ 버전 업데이트 (예: 1.0.0 → 1.1.0)
      ✅ CHANGELOG.md 생성
      ✅ 버전 커밋 자동 생성
      ✅ Affected apps 감지 (project-a, project-b)
      ✅ 빌드 및 배포

  3. 결과 확인

# develop 브랜치 최신화
git checkout develop
git pull origin develop

# 버전 확인
cat packages/react-hooks/package.json | grep version
# 예상: "version": "1.1.0"

# CHANGELOG 확인
cat packages/react-hooks/CHANGELOG.md

# 커밋 히스토리 확인
git log --oneline -5

결과:

🔍 시나리오 1 완료 검증
================================

1️⃣ 현재 브랜치:
develop

2️⃣ 패키지 버전:
  react-hooks: \Users\seungyeon\git\monorepo-test\packages\react-hooks\package.json

3️⃣ 파일 확인:
 useLocalStorage.ts 존재
 useLocalStorage.js (빌드) 존재

4️⃣ Changeset 상태:
 Changeset 처리 완료 (파일 삭제됨)

5️⃣ 최근 워크플로우:

conclusion name                  status   
---------- ----                  ------
success    Deploy to Development completed

 시나리오 1 완료!
================================

여기까지 했는데 오히려 더 복잡해지는 것 같아 그만함.

새롭게 짠 워크플로우 기반으로 진행

# ===== 시나리오 1: Project A 기능 추가 =====
git checkout -b feature/project-a-dashboard develop

# 1. 코드 수정
# apps/project-a/src/... 수정

# 2. Changeset 생성
pnpm changeset
# ? Which packages would you like to include?
# → @myapp/project-a 선택
# ? What kind of change is this for @myapp/project-a?
# → minor (새 기능)
# ? Summary: Added new dashboard widget

# 3. 생성된 changeset 파일
cat .changeset/strange-lions-jump.md
---
"@myapp/project-a": minor
---

Added new dashboard widget

# 4. Commit & Push
git add .
git commit -m "feat(project-a): add dashboard widget"
git push origin feature/project-a-dashboard

# 5. PR Merge 후 develop 브랜치에서
git checkout develop
git pull

# 6. 버전 업데이트 (develop에서 베타 버전)
pnpm changeset version --snapshot beta
# @myapp/project-a: 2.3.0 → 2.4.0-beta.0

git add .
git commit -m "chore: version packages (beta)"
git push

# 7. CI/CD가 자동으로 배포
# → Project A Development (v2.4.0-beta.0)

테스트 하다가 코드가 엉켜서 project-a만 테스트 해보려고 했는데, project-b까지 업데이트 됨.
보면 @test/project-a@0.0.0-beta-..., @test/project-b@0.0.0-beta..., @test/react-hooks@0.0.0-beta... 이렇게 업데이트 됨.

근데 왜 @test/react-hooks까지 업데이트 됐지? 난 얘 코드 변경한 적 없는데

changeset은 "어떤 패키지에서 코드가 변경되었는지"가 아니라, "현재 열려 있는 changeset 안에 어떤 패키지가 들어 있나"를 기준으로 버전을 올린다. 그래서 실제 코드 변경이 project-a, project-b에만 있어도, 이전에 만든 changeset에 @test/react-hooks가 포함되어 있었거나, 생성할 때 함께 체크했으면 올라감.

이전에 react-hooks를 변경한 changeset의 .md 파일이 같이 올라갔음.

image-20251203175236778

버전 업데이트는 문제가 없고, actions의 워크플로우 보면 변경된 패키지만 개발 환경 배포됨.
image-20251203181308915


시나리오 2. develop -> main 전체 배포


추가사항

이쯤돼서 궁금해지는 버전 vs. 릴리즈 vs. 태그

1. 버전

  • 코드의 "상태"를 나타내는 식별자
  • package.json 파일에 저장된 문자열
  • semantic versioning 규칙 따름 (major, minor, patch)

특징

  • 파일에 저장된 텍스트
  • 커밋할 때마다 변경 가능
  • 빌드된 앱에 포함됨
  • git과 독립적 (git이 없어도 존재)

변경 시점 (feat. changeset)

pnpm changeset version
  • package.json의 version 필드가 변경됨.
  • 아직 git에는 커밋 안 됨
git add .
git commit -m "chore: bump version to "

[!tip] prod 배포 실수 방지하기

  1. GitHub Environment 보호 규칙 쓰기 (가장 정석적인 방법)

  2. GitHub 리포지토리 → Settings → Environments → New environment

  • 이름: production

  • Required reviewers 를 1명 이상 지정

  1. 워크플로우 prod 잡에 environment 추가:

deploy-project-a-prod:

environment: production

...
  • 이렇게 하면:

  • prod 잡이 실행되더라도 중간에 멈춰서 “승인 대기” 상태가 됩니다.

  • 지정된 리뷰어가 GitHub Actions 화면에서 “Approve” 버튼을 눌러야만 계속 진행.

  • 사람 승인 없이는 절대 prod 가 끝까지 가지 않으니,

실수 배포를 막는 가장 GitHub-공식적인 방법입니다.